home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / VBSamples / Common / DplayCon.frm (.txt) < prev    next >
Encoding:
Visual Basic Form  |  2001-10-08  |  40.5 KB  |  970 lines

  1. VERSION 5.00
  2. Begin VB.Form DPlayConnect 
  3.    BorderStyle     =   3  'Fixed Dialog
  4.    Caption         =   "Multiplayer connect"
  5.    ClientHeight    =   3330
  6.    ClientLeft      =   45
  7.    ClientTop       =   330
  8.    ClientWidth     =   6345
  9.    Icon            =   "DplayCon.frx":0000
  10.    LinkTopic       =   "Form1"
  11.    MaxButton       =   0   'False
  12.    MinButton       =   0   'False
  13.    ScaleHeight     =   3330
  14.    ScaleWidth      =   6345
  15.    StartUpPosition =   3  'Windows Default
  16.    Begin VB.Frame fraWiz 
  17.       BorderStyle     =   0  'None
  18.       Height          =   3195
  19.       Index           =   1
  20.       Left            =   120
  21.       TabIndex        =   5
  22.       Top             =   60
  23.       Width           =   6195
  24.       Begin VB.CommandButton cmdCancelGame 
  25.          Caption         =   "Cancel"
  26.          Height          =   315
  27.          Left            =   5040
  28.          TabIndex        =   12
  29.          Top             =   2880
  30.          Width           =   1095
  31.       End
  32.       Begin VB.CommandButton cmdRefresh 
  33.          Caption         =   "S&tart Search"
  34.          Height          =   315
  35.          Left            =   5040
  36.          TabIndex        =   11
  37.          Top             =   60
  38.          Width           =   1095
  39.       End
  40.       Begin VB.CommandButton cmdJoin 
  41.          Caption         =   "&Join"
  42.          Height          =   315
  43.          Left            =   60
  44.          TabIndex        =   8
  45.          Top             =   2880
  46.          Width           =   1095
  47.       End
  48.       Begin VB.CommandButton cmdCreate 
  49.          Caption         =   "&Create"
  50.          Height          =   315
  51.          Left            =   1200
  52.          TabIndex        =   7
  53.          Top             =   2880
  54.          Width           =   1095
  55.       End
  56.       Begin VB.ListBox lstGames 
  57.          Height          =   2400
  58.          Left            =   60
  59.          TabIndex        =   6
  60.          Top             =   420
  61.          Width           =   6075
  62.       End
  63.       Begin VB.Label Label3 
  64.          BackStyle       =   0  'Transparent
  65.          Caption         =   "Click 'Start Search' to look for a session, or create one of your own."
  66.          Height          =   255
  67.          Left            =   60
  68.          TabIndex        =   25
  69.          Top             =   120
  70.          Width           =   4935
  71.       End
  72.    End
  73.    Begin VB.Frame fraWiz 
  74.       BorderStyle     =   0  'None
  75.       Height          =   3195
  76.       Index           =   0
  77.       Left            =   120
  78.       TabIndex        =   2
  79.       Top             =   60
  80.       Width           =   6195
  81.       Begin VB.CommandButton cmdCancel 
  82.          Cancel          =   -1  'True
  83.          Caption         =   "Cancel"
  84.          Height          =   315
  85.          Left            =   3900
  86.          TabIndex        =   10
  87.          Top             =   2760
  88.          Width           =   1095
  89.       End
  90.       Begin VB.CommandButton cmdOk 
  91.          Caption         =   "OK"
  92.          Default         =   -1  'True
  93.          Height          =   315
  94.          Left            =   5040
  95.          TabIndex        =   9
  96.          Top             =   2760
  97.          Width           =   1095
  98.       End
  99.       Begin VB.ListBox lstSP 
  100.          Height          =   1815
  101.          Left            =   60
  102.          TabIndex        =   4
  103.          Top             =   900
  104.          Width           =   6075
  105.       End
  106.       Begin VB.TextBox txtUserName 
  107.          Height          =   285
  108.          Left            =   60
  109.          TabIndex        =   1
  110.          Top             =   300
  111.          Width           =   6075
  112.       End
  113.       Begin VB.Label Label1 
  114.          BackStyle       =   0  'Transparent
  115.          Caption         =   "Select your ser&vice provider:"
  116.          Height          =   255
  117.          Index           =   1
  118.          Left            =   60
  119.          TabIndex        =   3
  120.          Top             =   660
  121.          Width           =   3915
  122.       End
  123.       Begin VB.Label Label1 
  124.          BackStyle       =   0  'Transparent
  125.          Caption         =   "&Player Name:"
  126.          Height          =   255
  127.          Index           =   0
  128.          Left            =   60
  129.          TabIndex        =   0
  130.          Top             =   60
  131.          Width           =   3915
  132.       End
  133.    End
  134.    Begin VB.Frame fraWiz 
  135.       BorderStyle     =   0  'None
  136.       Height          =   3195
  137.       Index           =   2
  138.       Left            =   120
  139.       TabIndex        =   13
  140.       Top             =   60
  141.       Width           =   6195
  142.       Begin VB.Frame Frame1 
  143.          Caption         =   "Extra Session Options"
  144.          Height          =   1995
  145.          Left            =   60
  146.          TabIndex        =   20
  147.          Top             =   660
  148.          Width           =   5955
  149.          Begin VB.CheckBox chkMigrate 
  150.             Alignment       =   1  'Right Justify
  151.             Caption         =   "Migrate &Host"
  152.             Height          =   255
  153.             Left            =   30
  154.             TabIndex        =   21
  155.             Top             =   600
  156.             Width           =   2895
  157.          End
  158.          Begin VB.TextBox txtUsers 
  159.             Height          =   285
  160.             Left            =   2490
  161.             TabIndex        =   19
  162.             Top             =   240
  163.             Width           =   435
  164.          End
  165.          Begin VB.Label Label1 
  166.             BackStyle       =   0  'Transparent
  167.             Caption         =   "N&umber of players in this session:"
  168.             Height          =   255
  169.             Index           =   2
  170.             Left            =   60
  171.             TabIndex        =   18
  172.             Top             =   285
  173.             Width           =   2355
  174.          End
  175.       End
  176.       Begin VB.TextBox txtGameName 
  177.          Height          =   285
  178.          Left            =   60
  179.          TabIndex        =   17
  180.          Top             =   300
  181.          Width           =   6075
  182.       End
  183.       Begin VB.CommandButton cmdOkCreate 
  184.          Caption         =   "OK"
  185.          Height          =   315
  186.          Left            =   5040
  187.          TabIndex        =   15
  188.          Top             =   2760
  189.          Width           =   1095
  190.       End
  191.       Begin VB.CommandButton cmdCancelCreate 
  192.          Caption         =   "Cancel"
  193.          Height          =   315
  194.          Left            =   3900
  195.          TabIndex        =   14
  196.          Top             =   2760
  197.          Width           =   1095
  198.       End
  199.       Begin VB.Label Label1 
  200.          BackStyle       =   0  'Transparent
  201.          Caption         =   "Please enter the session &name"
  202.          Height          =   255
  203.          Index           =   3
  204.          Left            =   60
  205.          TabIndex        =   16
  206.          Top             =   60
  207.          Width           =   2235
  208.       End
  209.    End
  210.    Begin VB.Timer tmrExpire 
  211.       Interval        =   500
  212.       Left            =   7500
  213.       Top             =   780
  214.    End
  215.    Begin VB.Frame fraWiz 
  216.       BorderStyle     =   0  'None
  217.       Height          =   3195
  218.       Index           =   3
  219.       Left            =   60
  220.       TabIndex        =   22
  221.       Top             =   60
  222.       Width           =   6195
  223.       Begin VB.CommandButton cmdCancelLobby 
  224.          Caption         =   "Cancel"
  225.          Height          =   315
  226.          Left            =   2340
  227.          TabIndex        =   24
  228.          Top             =   1500
  229.          Width           =   1455
  230.       End
  231.       Begin VB.Label Label2 
  232.          BackStyle       =   0  'Transparent
  233.          Caption         =   "Waiting for lobby connection"
  234.          Height          =   375
  235.          Left            =   2100
  236.          TabIndex        =   23
  237.          Top             =   1140
  238.          Width           =   2115
  239.       End
  240.    End
  241. Attribute VB_Name = "DPlayConnect"
  242. Attribute VB_GlobalNameSpace = False
  243. Attribute VB_Creatable = False
  244. Attribute VB_PredeclaredId = True
  245. Attribute VB_Exposed = False
  246. Option Explicit
  247. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  248. '  Copyright (C) 1999-2001 Microsoft Corporation.  All Rights Reserved.
  249. '  File:       DPlayCon.frm
  250. '''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
  251. 'Sleep declare
  252. Private Declare Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)
  253. 'GetTickCount declare
  254. Private Declare Function GetTickCount Lib "kernel32" () As Long
  255. 'Declares for closing the form without waiting
  256. Private Declare Function PostMessage Lib "user32" Alias "PostMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  257. Private Const WM_CLOSE = &H10
  258. 'Host expire threshold constant
  259. Private Const HOST_EXPIRE_THRESHHOLD As Long = 2000
  260. Private Type HostFound
  261.     AppDesc As DPN_APPLICATION_DESC
  262.     AddressHost As String
  263.     AddressDevice As String
  264.     TimeLastFound As Long
  265. End Type
  266. Private Enum WizPanes
  267.     PickProtocol
  268.     CreateJoinGame
  269.     CreateNewGame
  270.     WaitForLobby
  271. End Enum
  272. Private Enum SearchingButton
  273.     StartSearch
  274.     StopSearch
  275. End Enum
  276. 'Internal DirectX variables
  277. Private moDPP As DirectPlay8Peer
  278. Private moDPC As DirectPlay8Client
  279. Private moDPA As DirectPlay8Address
  280. Private moDX As DirectX8
  281. Private moCallback As DirectPlay8Event
  282. Private moDPLA As DirectPlay8LobbiedApplication
  283. 'App specific vars
  284. Private msGuid As String
  285. Private sUser As String
  286. Private mlSearch As SearchingButton
  287. Private sGameName As String
  288. Private mlMax As Long
  289. Private mlNumPlayers As Long
  290. Private mfComplete As Boolean
  291. Private mfHost As Boolean
  292. Private mlEnumAsync As Long
  293. Private mfGotEvent As Boolean
  294. Private mfDoneWiz As Boolean
  295. Private mlLobbyClientID As Long
  296. Private mfCanUnload As Boolean
  297. 'We need to keep track of the hosts we get
  298. Private moHosts() As HostFound
  299. Private mlHostCount As Long
  300. 'Declaration for our API
  301. Private Declare Function GetUserName Lib "advapi32.dll" Alias "GetUserNameA" (ByVal lpBuffer As String, nSize As Long) As Long
  302. Private mfDoneEnum As Boolean
  303. Private mfConnectComplete As Boolean
  304. 'We need to implement the Event model for DirectPlay so we can receive callbacks
  305. Implements DirectPlay8Event
  306. Implements DirectPlay8LobbyEvent
  307. Private Function StartWizard(oDX As DirectX8, sGuid As String, ByVal lMaxPlayers As Long, Optional oCallback As DirectPlay8Event = Nothing, Optional ByVal fAllowMigrateHost As Boolean = True) As Boolean
  308.     Dim lCount As Long, lIndex As Long
  309.     Dim dpn As DPN_SERVICE_PROVIDER_INFO
  310.     'Now we can start our connection
  311.     mfCanUnload = False
  312.     mlSearch = StartSearch
  313.     mlHostCount = -1
  314.     'First we need to keep track of our Peer Object, and app guid
  315.     Set moDX = oDX
  316.     Set moCallback = oCallback
  317.     msGuid = sGuid
  318.     mlMax = lMaxPlayers
  319.     lIndex = GetSetting("VBDirectPlay", "Defaults", "SPListIndex", -1)
  320.     If Not (moDPP Is Nothing) Then
  321.         moDPP.RegisterMessageHandler Me
  322.         'First load our list of Service Providers into our box
  323.         For lCount = 1 To moDPP.GetCountServiceProviders
  324.             dpn = moDPP.GetServiceProvider(lCount)
  325.             lstSP.AddItem dpn.Name
  326.             'Pick the TCP/IP connection by default
  327.             If InStr(dpn.Name, "TCP") Then lstSP.ListIndex = lstSP.ListCount - 1
  328.         Next
  329.         If lstSP.ListIndex < 0 Then lstSP.ListIndex = 0
  330.     ElseIf Not (moDPC Is Nothing) Then
  331.         moDPC.RegisterMessageHandler Me
  332.         'First load our list of Service Providers into our box
  333.         For lCount = 1 To moDPC.GetCountServiceProviders
  334.             dpn = moDPC.GetServiceProvider(lCount)
  335.             lstSP.AddItem dpn.Name
  336.             'Pick the TCP/IP connection by default
  337.             If InStr(dpn.Name, "TCP") Then lstSP.ListIndex = lstSP.ListCount - 1
  338.         Next
  339.         If lstSP.ListIndex < 0 Then lstSP.ListIndex = 0
  340.     End If
  341.     If lIndex <> -1 And lIndex < lstSP.ListCount Then lstSP.ListIndex = lIndex
  342.     lstSP.AddItem "Wait for Lobby Connection..."
  343.     'Load the default Username for VBDirectPlay samples
  344.     sUser = GetSetting("VBDirectPlay", "Defaults", "UserName", vbNullString)
  345.     If sUser = vbNullString Then
  346.         'If there is not a default username, then pick the currently
  347.         'logged on username
  348.         sUser = Space$(255)
  349.         GetUserName sUser, 255
  350.         sUser = Left$(sUser, InStr(sUser, Chr$(0)) - 1)
  351.     End If
  352.     chkMigrate.Visible = fAllowMigrateHost
  353.     txtUserName.Text = sUser
  354.     ShowPane PickProtocol
  355.     Set moDPLA = dx.DirectPlayLobbiedApplicationCreate
  356.     'Init the register handler here
  357.     moDPLA.RegisterMessageHandler Me
  358.     'Register this app (in case it isn't registered already)
  359.     RegisterThisApp sGuid
  360.     'Show this screen
  361.     Me.Show vbModeless
  362.     'We have this loop here rather than just displaying the form as a modal
  363.     'dialog if we did just display the form as modal, it would not get a
  364.     'button in the toolbar, since it would have a parent window that wasn't visible
  365.     'By displaying the window modeless, and going into a loop we get to have our
  366.     'icon on the taskbar, and keep the main form waiting until we are done in this form.
  367.     Do While Not mfDoneWiz
  368.         DoSleep 5 'Give other threads cpu time
  369.     Loop
  370.     'Get rid of the lobby interface if it isn't necessary
  371.     If mlLobbyClientID <> 0 Then
  372.         If Not (moDPP Is Nothing) Then
  373.             moDPP.RegisterLobby mlLobbyClientID, moDPLA, DPNLOBBY_UNREGISTER
  374.         ElseIf Not (moDPC Is Nothing) Then
  375.             moDPC.RegisterLobby mlLobbyClientID, moDPLA, DPNLOBBY_UNREGISTER
  376.         End If
  377.     End If
  378.     If Not (moDPLA Is Nothing) Then moDPLA.Close
  379.     Set moDPLA = Nothing
  380.     'Now we can return our success (or failure)
  381.     StartWizard = mfComplete
  382. End Function
  383. Public Function StartClientConnectWizard(oDX As DirectX8, oDPC As DirectPlay8Client, sGuid As String, ByVal lMaxPlayers As Long, Optional oCallback As DirectPlay8Event = Nothing, Optional ByVal fAllowMigrateHost As Boolean = True) As Boolean
  384.     Set moDPP = Nothing
  385.     Set moDPC = oDPC
  386.     cmdCreate.Visible = False
  387.     StartClientConnectWizard = StartWizard(oDX, sGuid, lMaxPlayers, oCallback, fAllowMigrateHost)
  388. End Function
  389. Public Function StartConnectWizard(oDX As DirectX8, oDPP As DirectPlay8Peer, sGuid As String, ByVal lMaxPlayers As Long, Optional oCallback As DirectPlay8Event = Nothing, Optional ByVal fAllowMigrateHost As Boolean = True) As Boolean
  390.     Set moDPC = Nothing
  391.     Set moDPP = oDPP
  392.     cmdCreate.Visible = True
  393.     StartConnectWizard = StartWizard(oDX, sGuid, lMaxPlayers, oCallback, fAllowMigrateHost)
  394. End Function
  395. Public Sub CloseForm(oForm As Form)
  396.     'Anytime we need to close a form from within a DirectPlay callback
  397.     'we need to use this function.  The reason is that DirectPlay uses multiple
  398.     'threads to spawn all of it's messages back to the application.  However
  399.     'it cannot close down until all of it's threads have returned.
  400.     'If we attempt to simply call Unload Me in the callback, we will run into
  401.     'a deadlock instance, since the callback will be running on the DirectPlay
  402.     'thread waiting for the unload to finish, and the unload will be waiting
  403.     'for the DirectPlay thread to finish.
  404.     'PostMessage puts the message on the queue for our form and returns immediately
  405.     'allowing the thread to finish
  406.     PostMessage oForm.hwnd, WM_CLOSE, 0, 0
  407. End Sub
  408. Public Sub DoSleep(Optional ByVal lMilliSec As Long = 0)
  409.     'The DoSleep function allows other threads to have a time slice
  410.     'and still keeps the main VB thread alive (since DPlay callbacks
  411.     'run on separate threads outside of VB).
  412.     Sleep lMilliSec
  413.     DoEvents
  414. End Sub
  415. Private Sub cmdCancel_Click()
  416.     Unload Me
  417. End Sub
  418. Private Sub cmdCancelCreate_Click()
  419.     'If they click cancel here, just go back to the last step
  420.     ShowPane CreateJoinGame
  421. End Sub
  422. Private Sub cmdCancelGame_Click()
  423.     'If they click cancel here, just go back to the first step
  424.     ShowPane PickProtocol
  425. End Sub
  426. Private Sub cmdCancelLobby_Click()
  427.     'Don't wait any more.
  428.     moDPLA.SetAppAvailable False, 0
  429.     ShowPane PickProtocol
  430. End Sub
  431. Private Sub cmdCreate_Click()
  432.     Dim sDefault As String
  433.     'Here we should get our default
  434.     sDefault = GetSetting("VBDirectPlay", "Defaults", "GameName", vbNullString)
  435.     txtGameName.Text = sDefault
  436.     txtUsers.Text = CStr(mlMax)
  437.     chkMigrate.Value = Val(GetSetting("VBDirectPlay", "Defaults", "HostMigrate"))
  438.     'Show the create game screen
  439.     ShowPane CreateNewGame
  440. End Sub
  441. Private Sub cmdJoin_Click()
  442.     Dim HostAddr As DirectPlay8Address
  443.     Dim DeviceAddr As DirectPlay8Address
  444.     Dim dpApp As DPN_APPLICATION_DESC
  445.     'You must select a game before you try to join one
  446.     If lstGames.ListIndex < 0 Then
  447.         MsgBox "You must first select a game from the list to join.", vbOKOnly Or vbInformation, "Select game."
  448.         Exit Sub
  449.     End If
  450.     'Lets join the game
  451.     Dim pInfo As DPN_PLAYER_INFO
  452.     'Set up my peer info
  453.     pInfo.Name = sUser
  454.     pInfo.lInfoFlags = DPNINFO_NAME
  455.     If Not (moDPP Is Nothing) Then
  456.         moDPP.SetPeerInfo pInfo, DPNOP_SYNC
  457.     ElseIf Not (moDPC Is Nothing) Then
  458.         moDPC.SetClientInfo pInfo, DPNOP_SYNC
  459.     End If
  460.     mfDoneEnum = True
  461.     With moHosts(lstGames.ItemData(lstGames.ListIndex)).AppDesc
  462.         dpApp.guidApplication = .guidApplication
  463.         dpApp.guidInstance = .guidInstance
  464.         mlNumPlayers = .lMaxPlayers
  465.     End With
  466.     mfGotEvent = False
  467.     mfConnectComplete = False
  468.     'Lets get our host address
  469.     If moHosts(lstGames.ItemData(lstGames.ListIndex)).AddressHost <> vbNullString Then
  470.         Set HostAddr = moDX.DirectPlayAddressCreate
  471.         HostAddr.BuildFromURL moHosts(lstGames.ItemData(lstGames.ListIndex)).AddressHost
  472.     Else
  473.         Set HostAddr = moDPA
  474.     End If
  475.     If moHosts(lstGames.ItemData(lstGames.ListIndex)).AddressDevice <> vbNullString Then
  476.         Set DeviceAddr = moDX.DirectPlayAddressCreate
  477.         DeviceAddr.BuildFromURL moHosts(lstGames.ItemData(lstGames.ListIndex)).AddressDevice
  478.     Else
  479.         Set DeviceAddr = moDPA
  480.     End If
  481.     If Not (moDPP Is Nothing) Then
  482.         'Now we can join the selected session
  483.         moDPP.Connect dpApp, HostAddr, DeviceAddr, DPNCONNECT_OKTOQUERYFORADDRESSING, ByVal 0&, 0
  484.     ElseIf Not (moDPC Is Nothing) Then
  485.         'Now we can join the selected session
  486.         moDPC.Connect dpApp, HostAddr, DeviceAddr, DPNCONNECT_OKTOQUERYFORADDRESSING, ByVal 0&, 0
  487.     End If
  488.     Do While Not mfGotEvent 'Let's wait for our connectcomplete event
  489.         DoSleep 5 'Give other threads cpu time
  490.     Loop
  491.     If mfConnectComplete Then
  492.         'We've joined our game
  493.         mfComplete = True
  494.         mfHost = False
  495.         'Clean up our address
  496.         Set HostAddr = Nothing
  497.         Set DeviceAddr = Nothing
  498.         Set moDPA = Nothing
  499.         Unload Me
  500.     End If
  501. End Sub
  502. Private Sub cmdOk_Click()
  503.     'They must specify a user name before we continue on to the next step
  504.     If txtUserName.Text = vbNullString Then
  505.         MsgBox "Please enter a username before going on.", vbOKOnly Or vbInformation, "No username"
  506.         Exit Sub
  507.     End If
  508.     sUser = txtUserName.Text
  509.     'Save the username
  510.     SaveSetting "VBDirectPlay", "Defaults", "UserName", sUser
  511.     SaveSetting "VBDirectPlay", "Defaults", "SPListIndex", lstSP.ListIndex
  512.     If lstSP.ListIndex = lstSP.ListCount - 1 Then 'We want to wait for a lobby connection
  513.         moDPLA.SetAppAvailable True, 0
  514.         ShowPane WaitForLobby
  515.     Else
  516.         'Set up the address
  517.         Set moDPA = moDX.DirectPlayAddressCreate
  518.         If Not (moDPP Is Nothing) Then
  519.             moDPA.SetSP moDPP.GetServiceProvider(lstSP.ListIndex + 1).Guid
  520.         ElseIf Not (moDPC Is Nothing) Then
  521.             moDPA.SetSP moDPC.GetServiceProvider(lstSP.ListIndex + 1).Guid
  522.         End If
  523.         'Switch to the next screen
  524.         ShowPane CreateJoinGame
  525.     End If
  526. End Sub
  527. Private Sub cmdOkCreate_Click()
  528.     sGameName = txtGameName.Text
  529.     If sGameName = vbNullString Then
  530.         MsgBox "You must enter a session name to create a session.", vbOKOnly Or vbInformation, "No name."
  531.         Exit Sub 'No need to continue
  532.     End If
  533.     If Val(txtUsers.Text) < 1 Then
  534.         MsgBox "You must enter a number of max players.", vbOKOnly Or vbInformation, "No max players."
  535.         Exit Sub 'No need to continue
  536.     End If
  537.     If Val(txtUsers.Text) > mlMax Then
  538.         MsgBox "The number of maximum players you specified exceeds the number of maximum players allowed in this session." & vbCrLf & "Please lower the number of your maximum players.", vbOKOnly Or vbInformation, "Too many players."
  539.         Exit Sub 'No need to continue
  540.     End If
  541.     mfHost = True
  542.     SaveSetting "VBDirectPlay", "Defaults", "GameName", sGameName
  543.     SaveSetting "VBDirectPlay", "Defaults", "HostMigrate", CStr(chkMigrate.Value)
  544.     If Not chkMigrate.Visible Then chkMigrate.Value = vbUnchecked
  545.     If Not CreateGame(sGameName, (chkMigrate.Value = vbChecked), Val(txtUsers.Text)) Then
  546.         MsgBox "Unable to create session", vbCritical Or vbOKOnly, "No session"
  547.         mfHost = False
  548.         ShowPane CreateJoinGame
  549.     Else
  550.         'We've created our game, load our game screen and wait for people to join
  551.         mfComplete = True
  552.         'Clean up our address
  553.         Set moDPA = Nothing
  554.         Unload Me
  555.     End If
  556. End Sub
  557. Private Sub cmdRefresh_Click()
  558.     If mlSearch = StartSearch Then
  559.         'Time to enum our hosts
  560.         mfDoneEnum = False
  561.         Dim Desc As DPN_APPLICATION_DESC
  562.         Desc.guidApplication = msGuid
  563.         
  564.         If Not (moDPP Is Nothing) Then
  565.             mlEnumAsync = moDPP.EnumHosts(Desc, Nothing, moDPA, INFINITE, 0, INFINITE, DPNENUMHOSTS_OKTOQUERYFORADDRESSING, ByVal 0&, 0)
  566.         ElseIf Not (moDPC Is Nothing) Then
  567.             mlEnumAsync = moDPC.EnumHosts(Desc, Nothing, moDPA, INFINITE, 0, INFINITE, DPNENUMHOSTS_OKTOQUERYFORADDRESSING, ByVal 0&, 0)
  568.         End If
  569.         cmdRefresh.Caption = "Stop Search"
  570.         mlSearch = StopSearch
  571.     ElseIf mlSearch = StopSearch Then
  572.         mfDoneEnum = True
  573.         If Not (moDPP Is Nothing) Then
  574.             If mlEnumAsync <> 0 Then moDPP.CancelAsyncOperation mlEnumAsync, 0
  575.         ElseIf Not (moDPC Is Nothing) Then
  576.             If mlEnumAsync <> 0 Then moDPC.CancelAsyncOperation mlEnumAsync, 0
  577.         End If
  578.         cmdRefresh.Caption = "Start Search"
  579.         mlSearch = StartSearch
  580.     End If
  581. End Sub
  582. Private Sub AddHostsToListBox(oHost As DPNMSG_ENUM_HOSTS_RESPONSE)
  583.     Dim lFound As Long
  584.     'Here we will add a host that was found to our list box (or ignore it
  585.     'if it's already been added)
  586.     If mfDoneEnum Then Exit Sub
  587.     If mlHostCount = -1 Then
  588.         'We have no hosts already. Clear our list, and add this one to the list.
  589.         lstGames.Clear
  590.         ReDim moHosts(0)
  591.         moHosts(0).AppDesc = oHost.ApplicationDescription
  592.         moHosts(0).AddressHost = oHost.AddressSenderUrl
  593.         moHosts(0).AddressDevice = oHost.AddressDeviceUrl
  594.         'Save the last time this host was found
  595.         moHosts(0).TimeLastFound = GetTickCount
  596.         With oHost.ApplicationDescription
  597.             lstGames.AddItem .SessionName & " - " & CStr(.lCurrentPlayers) & "/" & CStr(.lMaxPlayers) & " - Latency:" & CStr(oHost.lRoundTripLatencyMS) & " ms"
  598.         End With
  599.         lstGames.ItemData(0) = 0
  600.         mlHostCount = mlHostCount + 1
  601.     Else
  602.         Dim lCount As Long
  603.         Dim fFound As Boolean
  604.         
  605.         For lCount = 0 To mlHostCount
  606.             If moHosts(lCount).AppDesc.guidInstance = oHost.ApplicationDescription.guidInstance Then
  607.                 'Save the last time this host was found
  608.                 moHosts(lCount).TimeLastFound = GetTickCount
  609.                 fFound = True
  610.                 Exit For
  611.             End If
  612.         Next
  613.         
  614.         If Not fFound Then 'We need to add this to the list
  615.             ReDim Preserve moHosts(mlHostCount + 1)
  616.             moHosts(mlHostCount + 1).AppDesc = oHost.ApplicationDescription
  617.             moHosts(mlHostCount + 1).AddressHost = oHost.AddressSenderUrl
  618.             moHosts(mlHostCount + 1).AddressDevice = oHost.AddressDeviceUrl
  619.             With oHost.ApplicationDescription
  620.                 lstGames.AddItem .SessionName & " - " & CStr(.lCurrentPlayers) & "/" & CStr(.lMaxPlayers) & " - Latency:" & CStr(oHost.lRoundTripLatencyMS) & " ms"
  621.             End With
  622.             'Save the last time this host was found
  623.             moHosts(mlHostCount + 1).TimeLastFound = GetTickCount
  624.             lstGames.ItemData(lstGames.ListCount - 1) = mlHostCount + 1
  625.             mlHostCount = mlHostCount + 1
  626.         Else 'We did find it, update the list
  627.             For lFound = 0 To lstGames.ListCount - 1
  628.                 With oHost.ApplicationDescription
  629.                 If lstGames.ItemData(lFound) = lCount Then 'This is it
  630.                     lstGames.List(lFound) = .SessionName & " - " & CStr(.lCurrentPlayers) & "/" & CStr(.lMaxPlayers) & " - Latency:" & CStr(oHost.lRoundTripLatencyMS) & " ms"
  631.                 End If
  632.                 End With
  633.             Next
  634.         End If
  635.     End If
  636. End Sub
  637. 'We will handle all of the msgs here, and report them all back to the callback sub
  638. 'in case the caller cares what's going on
  639. Private Sub DirectPlay8Event_AddRemovePlayerGroup(ByVal lMsgID As Long, ByVal lPlayerID As Long, ByVal lGroupID As Long, fRejectMsg As Boolean)
  640.     'VB requires that we must implement *every* member of this interface
  641.     If (Not moCallback Is Nothing) Then moCallback.AddRemovePlayerGroup lMsgID, lPlayerID, lGroupID, fRejectMsg
  642. End Sub
  643. Private Sub DirectPlay8Event_AppDesc(fRejectMsg As Boolean)
  644.     'VB requires that we must implement *every* member of this interface
  645.     If (Not moCallback Is Nothing) Then moCallback.AppDesc fRejectMsg
  646. End Sub
  647. Private Sub DirectPlay8Event_AsyncOpComplete(dpnotify As DxVBLibA.DPNMSG_ASYNC_OP_COMPLETE, fRejectMsg As Boolean)
  648.     If dpnotify.AsyncOpHandle = mlEnumAsync Then mlEnumAsync = 0
  649.     'VB requires that we must implement *every* member of this interface
  650.     If (Not moCallback Is Nothing) Then moCallback.AsyncOpComplete dpnotify, fRejectMsg
  651. End Sub
  652. Private Sub DirectPlay8Event_ConnectComplete(dpnotify As DxVBLibA.DPNMSG_CONNECT_COMPLETE, fRejectMsg As Boolean)
  653.     mfGotEvent = True
  654.     If dpnotify.hResultCode = DPNERR_SESSIONFULL Then 'Already too many people joined up
  655.         MsgBox "The maximum number of people allowed in this session have already joined.  Please choose a different session or create your own.", vbOKOnly Or vbInformation, "Full"
  656.         ShowPane CreateJoinGame
  657.     Else
  658.         'We got our connect complete event
  659.         mfConnectComplete = True
  660.         'VB requires that we must implement *every* member of this interface
  661.         If (Not moCallback Is Nothing) Then moCallback.ConnectComplete dpnotify, fRejectMsg
  662.     End If
  663. End Sub
  664. Private Sub DirectPlay8Event_CreateGroup(ByVal lGroupID As Long, ByVal lOwnerID As Long, fRejectMsg As Boolean)
  665.     'VB requires that we must implement *every* member of this interface
  666.     If (Not moCallback Is Nothing) Then moCallback.CreateGroup lGroupID, lOwnerID, fRejectMsg
  667. End Sub
  668. Private Sub DirectPlay8Event_CreatePlayer(ByVal lPlayerID As Long, fRejectMsg As Boolean)
  669.     'VB requires that we must implement *every* member of this interface
  670.     If (Not moCallback Is Nothing) Then moCallback.CreatePlayer lPlayerID, fRejectMsg
  671. End Sub
  672. Private Sub DirectPlay8Event_DestroyGroup(ByVal lGroupID As Long, ByVal lReason As Long, fRejectMsg As Boolean)
  673.     'VB requires that we must implement *every* member of this interface
  674.     If (Not moCallback Is Nothing) Then moCallback.DestroyGroup lGroupID, lReason, fRejectMsg
  675. End Sub
  676. Private Sub DirectPlay8Event_DestroyPlayer(ByVal lPlayerID As Long, ByVal lReason As Long, fRejectMsg As Boolean)
  677.     'VB requires that we must implement *every* member of this interface
  678.     If (Not moCallback Is Nothing) Then moCallback.DestroyPlayer lPlayerID, lReason, fRejectMsg
  679. End Sub
  680. Private Sub DirectPlay8Event_EnumHostsQuery(dpnotify As DxVBLibA.DPNMSG_ENUM_HOSTS_QUERY, fRejectMsg As Boolean)
  681.     'VB requires that we must implement *every* member of this interface
  682.     If (Not moCallback Is Nothing) Then moCallback.EnumHostsQuery dpnotify, fRejectMsg
  683. End Sub
  684. Private Sub DirectPlay8Event_EnumHostsResponse(dpnotify As DxVBLibA.DPNMSG_ENUM_HOSTS_RESPONSE, fRejectMsg As Boolean)
  685.     'Go ahead and add this to our list
  686.     AddHostsToListBox dpnotify
  687.     'VB requires that we must implement *every* member of this interface
  688.     If (Not moCallback Is Nothing) Then moCallback.EnumHostsResponse dpnotify, fRejectMsg
  689. End Sub
  690. Private Sub DirectPlay8Event_HostMigrate(ByVal lNewHostID As Long, fRejectMsg As Boolean)
  691.     'VB requires that we must implement *every* member of this interface
  692.     If (Not moCallback Is Nothing) Then moCallback.HostMigrate lNewHostID, fRejectMsg
  693. End Sub
  694. Private Sub DirectPlay8Event_IndicateConnect(dpnotify As DxVBLibA.DPNMSG_INDICATE_CONNECT, fRejectMsg As Boolean)
  695.     'VB requires that we must implement *every* member of this interface
  696.     If (Not moCallback Is Nothing) Then moCallback.IndicateConnect dpnotify, fRejectMsg
  697. End Sub
  698. Private Sub DirectPlay8Event_IndicatedConnectAborted(fRejectMsg As Boolean)
  699.     'VB requires that we must implement *every* member of this interface
  700.     If (Not moCallback Is Nothing) Then moCallback.IndicatedConnectAborted fRejectMsg
  701. End Sub
  702. Private Sub DirectPlay8Event_InfoNotify(ByVal lMsgID As Long, ByVal lNotifyID As Long, fRejectMsg As Boolean)
  703.     'VB requires that we must implement *every* member of this interface
  704.     If (Not moCallback Is Nothing) Then moCallback.InfoNotify lMsgID, lNotifyID, fRejectMsg
  705. End Sub
  706. Private Sub DirectPlay8Event_Receive(dpnotify As DxVBLibA.DPNMSG_RECEIVE, fRejectMsg As Boolean)
  707.     'VB requires that we must implement *every* member of this interface
  708.     If (Not moCallback Is Nothing) Then moCallback.Receive dpnotify, fRejectMsg
  709. End Sub
  710. Private Sub DirectPlay8Event_SendComplete(dpnotify As DxVBLibA.DPNMSG_SEND_COMPLETE, fRejectMsg As Boolean)
  711.     'VB requires that we must implement *every* member of this interface
  712.     If (Not moCallback Is Nothing) Then moCallback.SendComplete dpnotify, fRejectMsg
  713. End Sub
  714. Private Sub DirectPlay8Event_TerminateSession(dpnotify As DxVBLibA.DPNMSG_TERMINATE_SESSION, fRejectMsg As Boolean)
  715.     'VB requires that we must implement *every* member of this interface
  716.     If (Not moCallback Is Nothing) Then moCallback.TerminateSession dpnotify, fRejectMsg
  717. End Sub
  718. Private Sub DirectPlay8LobbyEvent_Connect(dlNotify As DxVBLibA.DPL_MESSAGE_CONNECT, fRejectMsg As Boolean)
  719.     Dim oDev As DirectPlay8Address, oHost As DirectPlay8Address
  720.     Dim oSetting As DPL_CONNECTION_SETTINGS
  721.     Dim pInfo As DPN_PLAYER_INFO
  722.     On Local Error GoTo ErrOut
  723.     mlLobbyClientID = dlNotify.ConnectId
  724.     oSetting = moDPLA.GetConnectionSettings(mlLobbyClientID, 0)
  725.     'We were just connected to from a lobby
  726.     With oSetting
  727.     If Not (moDPP Is Nothing) Then
  728.         moDPP.RegisterLobby mlLobbyClientID, moDPLA, DPNLOBBY_REGISTER
  729.     ElseIf Not (moDPC Is Nothing) Then
  730.         moDPC.RegisterLobby mlLobbyClientID, moDPLA, DPNLOBBY_REGISTER
  731.     End If
  732.     'With ConnectionSettings
  733.     If .PlayerName <> vbNullString Then
  734.         sUser = .PlayerName
  735.         'Am I the host
  736.         If (.lFlags And DPLCONNECTSETTINGS_HOST) = DPLCONNECTSETTINGS_HOST Then
  737.             'Get the device address to host on
  738.             Set oDev = moDX.DirectPlayAddressCreate
  739.             oDev.BuildFromURL dlNotify.dplMsgCon.AddressSenderUrl
  740.             If Not CreateGameLobby(oDev, .ApplicationDescription) Then
  741.                 MsgBox "Unable to create session", vbCritical Or vbOKOnly, "No session"
  742.                 mfHost = False
  743.                 ShowPane CreateJoinGame
  744.             Else
  745.                 'We've created our game, load our game screen and wait for people to join
  746.                 mfHost = True 'We are the host
  747.                 mfComplete = True
  748.                 'Clean up our address
  749.                 Set moDPA = Nothing
  750.                 Unload Me
  751.             End If
  752.         Else 'we want to connect to an running app
  753.             sUser = .PlayerName
  754.             'Set up my peer info
  755.             pInfo.Name = sUser
  756.             pInfo.lInfoFlags = DPNINFO_NAME
  757.             'Go ahead and connect
  758.             Set oDev = moDX.DirectPlayAddressCreate
  759.             oDev.BuildFromURL dlNotify.dplMsgCon.AddressSenderUrl
  760.             Set oHost = moDX.DirectPlayAddressCreate
  761.             oHost.BuildFromURL dlNotify.dplMsgCon.AddressDeviceUrl
  762.             If Not (moDPP Is Nothing) Then
  763.                 moDPP.SetPeerInfo pInfo, DPNOP_SYNC
  764.                 'Connect now
  765.                 moDPP.Connect .ApplicationDescription, oHost, oDev, 0, ByVal 0&, 0
  766.             ElseIf Not (moDPC Is Nothing) Then
  767.                 moDPC.SetClientInfo pInfo, DPNOP_SYNC
  768.                 'Connect now
  769.                 moDPC.Connect .ApplicationDescription, oHost, oDev, 0, ByVal 0&, 0
  770.             End If
  771.             'Now we should wait until the connect complete event has fired
  772.             Do While Not mfConnectComplete
  773.                 DoEvents
  774.                 'We need to sleep here since the Directplay callbacks run on separate
  775.                 'threads, and a tight loop with only doevents will not allow them enough
  776.                 'time to call into VB.  Sleep 'pauses' this thread for a short time,
  777.                 'allowing the callbacks to process
  778.                 Sleep 10
  779.             Loop
  780.             'We've joined our game
  781.             mfComplete = True
  782.             mfHost = False
  783.             'Clean up our address
  784.             Set moDPA = Nothing
  785.             Unload Me
  786.             
  787.         End If
  788.     Else
  789.         ShowPane PickProtocol
  790.     End If
  791.     End With
  792.     Exit Sub
  793. ErrOut:
  794.     Debug.Print "Error:" & CStr(Err.Number) & " - " & Err.Description
  795. End Sub
  796. Private Sub DirectPlay8LobbyEvent_ConnectionSettings(ConnectionSettings As DxVBLibA.DPL_MESSAGE_CONNECTION_SETTINGS)
  797.     'VB requires that we must implement *every* member of this interface
  798. End Sub
  799. Private Sub DirectPlay8LobbyEvent_Disconnect(ByVal DisconnectID As Long, ByVal lReason As Long)
  800.     'VB requires that we must implement *every* member of this interface
  801. End Sub
  802. Private Sub DirectPlay8LobbyEvent_Receive(dlNotify As DxVBLibA.DPL_MESSAGE_RECEIVE, fRejectMsg As Boolean)
  803.     'VB requires that we must implement *every* member of this interface
  804. End Sub
  805. Private Sub DirectPlay8LobbyEvent_SessionStatus(ByVal status As Long, ByVal lHandle As Long)
  806.     'VB requires that we must implement *every* member of this interface
  807. End Sub
  808. Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
  809.     If Not mfCanUnload Then Cancel = 1
  810.     Me.Hide
  811.     mfDoneWiz = True
  812. End Sub
  813. Private Sub Form_Unload(Cancel As Integer)
  814.     'Clean up our lobbied app
  815.     If Not (moDPLA Is Nothing) Then
  816.         moDPLA.Close
  817.     End If
  818.     Set moDPLA = Nothing
  819.     'Clean up our address
  820.     Set moDPA = Nothing
  821. End Sub
  822. Private Sub lstGames_DblClick()
  823.     cmdJoin_Click
  824. End Sub
  825. Private Function CreateGame(ByVal sGameName As String, ByVal fHostMigrate As Boolean, ByVal lNumPlayers As Long) As Boolean
  826.     On Error GoTo ErrOut
  827.     'We want to host our own game
  828.     Dim pInfo As DPN_PLAYER_INFO
  829.     Dim AppDesc As DPN_APPLICATION_DESC
  830.     'Now set up the app description
  831.     With AppDesc
  832.         .guidApplication = msGuid
  833.         .lMaxPlayers = lNumPlayers
  834.         .SessionName = sGameName
  835.         If fHostMigrate Then
  836.             .lFlags = .lFlags Or DPNSESSION_MIGRATE_HOST
  837.         End If
  838.     End With
  839.     mlNumPlayers = lNumPlayers
  840.     'Set up my peer info
  841.     pInfo.Name = sUser
  842.     pInfo.lInfoFlags = DPNINFO_NAME
  843.     moDPP.SetPeerInfo pInfo, DPNOP_SYNC
  844.     moDPP.Host AppDesc, moDPA, DPNHOST_OKTOQUERYFORADDRESSING
  845.     CreateGame = True
  846.     Exit Function
  847. ErrOut:
  848.     CreateGame = False
  849.     Debug.Print "Error;"; Err; " - "; Err.Description
  850. End Function
  851. Private Sub lstSP_DblClick()
  852.     cmdOk_Click
  853. End Sub
  854. Public Property Get IsHost() As Boolean
  855.     IsHost = mfHost
  856. End Property
  857. Public Property Get SessionName() As String
  858.     SessionName = sGameName
  859. End Property
  860. Public Property Get UserName() As String
  861.     UserName = sUser
  862. End Property
  863. Public Sub GoUnload()
  864.     tmrExpire.Enabled = False
  865.     mfCanUnload = True
  866.     Unload Me
  867. End Sub
  868. Public Sub RegisterCallback(oCallback As DirectPlay8Event)
  869.     Set moCallback = oCallback
  870. End Sub
  871. Public Property Get NumPlayers() As Long
  872.     NumPlayers = mlNumPlayers
  873. End Property
  874. Private Sub ShowPane(ByVal lIndex As WizPanes)
  875.     'Here we will show the correct pane, and do whatever else
  876.     'we might need to do to get the step set up.
  877.     fraWiz(lIndex).ZOrder
  878.     Select Case lIndex
  879.     Case PickProtocol
  880.         cmdOk.Default = True
  881.         cmdCancel.Cancel = True
  882.         Me.Caption = App.EXEName & " - Choose Protocol"
  883.     Case CreateJoinGame
  884.         cmdCancelGame.Cancel = True
  885.         Me.Caption = App.EXEName & " - Create or Join Session"
  886.     Case CreateNewGame
  887.         cmdOkCreate.Default = True
  888.         cmdCancelCreate.Cancel = True
  889.         txtGameName.SetFocus
  890.         Me.Caption = App.EXEName & " - Create Session"
  891.     Case WaitForLobby
  892.         cmdCancelLobby.Cancel = True
  893.         cmdCancelLobby.Default = True
  894.         cmdCancelLobby.SetFocus
  895.         Me.Caption = App.EXEName & " - Wait for lobby connection"
  896.     End Select
  897. End Sub
  898. Private Function CreateGameLobby(oHostAddr As DirectPlay8Address, newDesc As DPN_APPLICATION_DESC) As Boolean
  899.     On Error GoTo ErrOut
  900.     'We want to host our own game
  901.     Dim pInfo As DPN_PLAYER_INFO
  902.     'Set up my peer info
  903.     pInfo.Name = sUser
  904.     pInfo.lInfoFlags = DPNINFO_NAME
  905.     'We are only using the Peer object, since the client object *can't* host a session
  906.     moDPP.SetPeerInfo pInfo, DPNOP_SYNC
  907.     newDesc.lFlags = newDesc.lFlags Or DPNSESSION_MIGRATE_HOST 'Turn on Migrate host by default
  908.     newDesc.lMaxPlayers = mlMax 'Let the max players join
  909.     sGameName = newDesc.SessionName
  910.     moDPP.Host newDesc, oHostAddr
  911.     CreateGameLobby = True
  912.     Exit Function
  913. ErrOut:
  914.     CreateGameLobby = False
  915.     Debug.Print "Error;" & CStr(Err.Number) & " - " & Err.Description
  916. End Function
  917. Private Sub RegisterThisApp(sGuid As String)
  918.     Dim dplProg As DPL_PROGRAM_DESC
  919.     'We need to register this program in case we aren't already registered
  920.     With dplProg
  921.         .ApplicationName = App.EXEName
  922.         .Description = "VB DirectPlay SDK Sample"
  923.         .ExecutableFilename = App.EXEName & ".exe"
  924.         .ExecutablePath = App.Path
  925.         .LauncherFilename = App.EXEName & ".exe"
  926.         .LauncherPath = App.Path
  927.         .guidApplication = sGuid
  928.     End With
  929.     moDPLA.RegisterProgram dplProg, 0
  930. End Sub
  931. Private Sub tmrExpire_Timer()
  932.     'We need to periodically expire the hosts that are in this list in case they are
  933.     'no longer hosting or what have you.
  934.     Dim lCount As Long, lIndex As Long
  935.     Dim lInner As Long
  936.     On Error GoTo LeaveSub 'If there are no hosts, just go
  937.     For lCount = 0 To UBound(moHosts)
  938.         If (GetTickCount - moHosts(lCount).TimeLastFound) > HOST_EXPIRE_THRESHHOLD Then
  939.             'Yup, this guy expired.. remove him from the list
  940.             For lIndex = lstGames.ListCount - 1 To 0 Step -1
  941.                 If lstGames.ItemData(lIndex) = lCount Then 'this is the one
  942.                     lstGames.RemoveItem lIndex
  943.                 End If
  944.             Next
  945.             moHosts(lCount).AddressDevice = vbNullString
  946.             moHosts(lCount).AddressHost = vbNullString
  947.             'Now we need an internal loop to 'remove' all of the old hosts info
  948.             For lInner = lCount + 1 To UBound(moHosts)
  949.                 moHosts(lInner - 1).AddressDevice = moHosts(lInner).AddressDevice
  950.                 moHosts(lInner - 1).AddressHost = moHosts(lInner).AddressHost
  951.                 moHosts(lInner - 1).AppDesc = moHosts(lInner).AppDesc
  952.                 moHosts(lInner - 1).TimeLastFound = moHosts(lInner).TimeLastFound
  953.             Next
  954.             'Now we need to decrement each of the remaining items in the listbox
  955.             For lIndex = lstGames.ListCount - 1 To 0 Step -1
  956.                 If lstGames.ItemData(lIndex) > lCount Then 'decrement this one
  957.                     lstGames.ItemData(lIndex) = lstGames.ItemData(lIndex) - 1
  958.                 End If
  959.             Next
  960.             mlHostCount = mlHostCount - 1
  961.             If UBound(moHosts) > 0 Then
  962.                 ReDim Preserve moHosts(UBound(moHosts) - 1)
  963.             Else
  964.                 Erase moHosts 'This will just erase the memory
  965.             End If
  966.         End If
  967.     Next
  968. LeaveSub:
  969. End Sub
  970.